use windows_sys::Win32::{
    Foundation::{GetLastError, LPARAM, LRESULT, POINT, WPARAM},
    System::LibraryLoader::{GetModuleHandleA, GetModuleHandleW},
    UI::Input::KeyboardAndMouse::GetAsyncKeyState,
    UI::WindowsAndMessaging::{
        CallNextHookEx, DispatchMessageW, GetMessageW, SetWindowsHookExW, TranslateMessage,
        UnhookWindowsHookEx, HHOOK, KBDLLHOOKSTRUCT, MSG, WH_KEYBOARD_LL,
    },
};


//[target.'cfg(target_os = "windows")'.dependencies]
//# windows-rs这个crate没有对Windows API进行封装，而是直接从kernel32.dll中获取然后调用
//windows-sys= {version = "0.52",features = ["Win32_Foundation", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp", "Win32_UI_WindowsAndMessaging",
//    "Win32_System_Threading", "Win32_System_LibraryLoader", "Win32_UI_Input_KeyboardAndMouse"] }

// 保存句柄
static mut HOOK_HANDLE: isize = -1;

pub fn set_hook() -> bool {
    unsafe {
        unset_hook();

        // let name=std::env::current_exe().unwrap().file_name().unwrap();

        println!(
            "{:?}",
            std::env::current_exe().unwrap().file_name().unwrap()
        );
        // https://learn.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew
        // 如果此参数为 NULL， 则 GetModuleHandle 返回用于创建调用进程 (.exe 文件) 的文件的句柄
        // let h_instance = GetModuleHandleW(windows_sys::w!("tauritest240709.exe"));
        // println!("a:{}",h_instance);
        let h_instance = GetModuleHandleW(std::ptr::null());
        // println!("b:{}",h_instance);
        // 126 找不到指定模块
        // let h_instance = GetModuleHandleW(windows_sys::w!("msedgewebview2.exe"));
        // println!("set  WH_KEYBOARD_LL hook success !!! {}",h_instance);
        println!("set  WH_KEYBOARD_LL hook success !!! {}", h_instance);
        if h_instance == 0 {
            eprintln!("Failed to SetWindowsHookExW: {}", GetLastError());
            return false;
        }
        // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexw
        HOOK_HANDLE = SetWindowsHookExW(
            WH_KEYBOARD_LL,
            Some(low_level_keyboard_proc),
            h_instance, //这个参数也可以之间传0
            0,
        );
        println!("set  WH_KEYBOARD_LL hook success 222!!! ");
        if HOOK_HANDLE == 0 {
            eprintln!("Failed to SetWindowsHookExW: {}", GetLastError());
            return false;
        }

        println!("set  WH_KEYBOARD_LL hook success !!!");
        // let mut msg: MSG = MSG {
        //     hwnd: 0,
        //     message: 0,
        //     wParam: 0,
        //     lParam: 0,
        //     time: 0,
        //     pt: POINT { x: 0, y: 0 },
        // };
        // // 在Hook线程中, 无论是用的哪种钩子,都需要一个消息循环轮询当前线程中的消息队列:
        // while GetMessageW(&mut msg, 0, 0, 0) != 0 {
        //     TranslateMessage(&msg);
        //     DispatchMessageW(&msg);
        // }
        return true;
    }
}

pub fn unset_hook() -> bool {
    unsafe {
        // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-unhookwindowshookex
        // 成功，则返回值为非零值
        // 函数失败，则返回值为零
        println!(
            "unset_hook  WH_KEYBOARD_LL hook success !!! {}",
            HOOK_HANDLE
        );
        if HOOK_HANDLE > 0 && UnhookWindowsHookEx(HOOK_HANDLE) == 0 {
            eprintln!("Failed to UnhookWindowsHookEx: {}", GetLastError());
            return false;
        }
        HOOK_HANDLE = -1;
    }
    true
}

/// 参考 https://learn.microsoft.com/zh-cn/windows/win32/winmsg/lowlevelkeyboardproc
/// n_code 小于零，则挂钩过程必须将消息传递给 CallNextHookEx 函数，而无需进一步处理，并且应返回 CallNextHookEx 返回的值
/// w_param 键盘消息的标识符。此参数可以是以下消息之一： WM_KEYDOWN、 WM_KEYUP、 WM_SYSKEYDOWN或 WM_SYSKEYUP
/// l_param 指向 KBDLLHOOKSTRUCT 结构的指针
/// 如果 nCode 小于零，则挂钩过程必须返回 CallNextHookEx 返回的值。
/// 如果 nCode 大于或等于零，并且挂钩过程未处理消息，强烈建议调用 CallNextHookEx 并返回它返回的值;否则，安装 WH_KEYBOARD_LL 挂钩的其他应用程序将不会收到挂钩通知，因此行为可能不正确
unsafe extern "system" fn low_level_keyboard_proc(
    n_code: i32,
    w_param: WPARAM,
    l_param: LPARAM,
) -> LRESULT {
    if n_code >= 0 {
        let hook_struct = *(l_param as *const KBDLLHOOKSTRUCT);
        // Handle keyboard events here

        println!("keycode --->{}", hook_struct.vkCode);

        // Check each condition and return 1 to block the key combination if matched
        if hook_struct.vkCode == 91 {
            // Left Win key
            return 1;
        }
        if hook_struct.vkCode == 92 {
            // Right Win key
            return 1;
        }
        if hook_struct.vkCode == 27 && GetAsyncKeyState(0x11) != 0 {
            // Ctrl + Esc
            return 1;
        }
        if hook_struct.vkCode == 115 && GetAsyncKeyState(0x12) != 0 {
            // Alt + F4
            return 1;
        }
        if hook_struct.vkCode == 9 && GetAsyncKeyState(0x12) != 0 {
            // Alt + Tab
            return 1;
        }
        if hook_struct.vkCode == 27 && GetAsyncKeyState(0x11) != 0 && GetAsyncKeyState(0x10) != 0 {
            // Ctrl + Shift + Esc
            return 1;
        }
        if hook_struct.vkCode == 32 && GetAsyncKeyState(0x12) != 0 {
            // Alt + Space
            return 1;
        }
        if hook_struct.vkCode == 112 {
            // F1
            return 1;
        }
        if GetAsyncKeyState(0x11) != 0 && GetAsyncKeyState(0x12) != 0 && hook_struct.vkCode == 46 {
            // Ctrl + Alt + Delete
            return 1;
        }
        if GetAsyncKeyState(0x11) != 0 && GetAsyncKeyState(0x10) != 0 {
            // Ctrl + Shift
            return 1;
        }
    }
    return CallNextHookEx(HOOK_HANDLE, n_code, w_param, l_param);
}
